// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "verible/verilog/tools/kythe/indexing-facts-tree-extractor.h"

#include <functional>
#include <string>
#include <string_view>
#include <vector>

#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "gtest/gtest.h"
#include "verible/common/analysis/syntax-tree-search-test-utils.h"
#include "verible/common/util/file-util.h"
#include "verible/common/util/logging.h"
#include "verible/common/util/tree-operations.h"
#include "verible/verilog/analysis/verilog-project.h"
#include "verible/verilog/tools/kythe/indexing-facts-tree.h"
#include "verible/verilog/tools/kythe/verilog-extractor-indexing-fact-type.h"

#undef EXPECT_OK
#define EXPECT_OK(value) EXPECT_TRUE((value).ok())

namespace verilog {
namespace kythe {
namespace {

using verible::file::testing::ScopedTestFile;

using T = IndexingFactNode;
using D = IndexingNodeData;

// This class exists, solely to store the temp dir name in a variable that
// outlives its uses.  Do not pass TempDir() directly anywhere that expects a
// string_view to be owned elsewhere, because it returns a temporary string,
// which would leave a dangling reference.
class TempDir {
 protected:
  TempDir() : temp_dir_(::testing::TempDir()) {}

 protected:
  const std::string temp_dir_;
};

// Groups information for a single temporary source file together.
struct TestFileEntry {
  // The original source code of the translation.
  const std::string_view origin_text;

  // One file in project, will be given randomly generated name.
  const ScopedTestFile temp_file;

  // Points to a successfully opened source file that corresponds to test_file_
  // opened as a translation unit.
  const VerilogSourceFile *const source_file = nullptr;

  TestFileEntry(std::string_view code_text, std::string_view temp_dir,
                const std::function<const VerilogSourceFile *(std::string_view)>
                    &file_opener,
                std::string_view override_basename = "")
      : origin_text(code_text),
        temp_file(temp_dir, origin_text, override_basename),
        // Open this file from inside 'project', save pointer here.
        source_file(file_opener(temp_file.filename())) {}

  // Returns the string_view of text owned by this->source_file.
  std::string_view SourceText() const {
    CHECK(source_file != nullptr);
    return source_file->GetContent();
  }

  T::value_type ExpectedFileData() const {
    CHECK(source_file != nullptr);
    return {
        IndexingFactType::kFile,
        Anchor(source_file->ResolvedPath()),
        Anchor(SourceText()),
    };
  }
};

// SimpleTestProject create a single-file project for testing.
// No include files supported in this base class.
class SimpleTestProject : public TempDir, public VerilogProject {
 public:
  // 'code_text' is the contents of the single translation unit in this project
  explicit SimpleTestProject(std::string_view code_text,
                             const std::vector<std::string> &include_paths = {})
      : VerilogProject(temp_dir_, include_paths, /*corpus=*/"unittest",
                       /*provide_lookup_file_origin=*/false),
        code_text_(code_text),
        translation_unit_(code_text, temp_dir_,
                          [this](std::string_view full_file_name)
                              -> const VerilogSourceFile * {
                            /* VerilogProject base class is already fully
                             * initialized */
                            return *OpenTranslationUnit(
                                verible::file::Basename(full_file_name));
                          }) {}

  const TestFileEntry &XUnit() const { return translation_unit_; }

  std::string_view OnlyFileName() const {
    return translation_unit_.temp_file.filename();
  }

  T Extract() {
    return ExtractFiles(
        /* file_list_path= */ temp_dir_,  // == Dirname(OnlyFileName()),
        /* project= */ this,
        /* file_names= */
        {std::string(verible::file::Basename(OnlyFileName()))}, &errors_);
  }

  T::value_type ExpectedFileListData() const {
    return {
        IndexingFactType::kFileList,  //
        Anchor(std::string_view(temp_dir_)),
        // file_list is co-located with the files it references
        Anchor(TranslationUnitRoot()),
        // TranslationUnitRoot() == verible::file::Dirname(OnlyFileName())
        // but in a different string_view range.
    };
  }

  std::vector<absl::Status> &Errors() { return errors_; }

  // This printer only works well for tests cases with one translation unit.
  PrintableIndexingFactNode TreePrinter(const IndexingFactNode &n) const {
    return PrintableIndexingFactNode(n, code_text_);
  }

 private:
  std::string code_text_;

  // The one and only translation unit under test.
  TestFileEntry translation_unit_;

  // Processing errors.
  std::vector<absl::Status> errors_;
};

// Test project with one source translation unit, one included file.
class IncludeTestProject : protected SimpleTestProject {
 public:
  // 'code_text' is the contents of the single translation unit in this project.
  // 'include_text' is the contents of the single included file in this project.
  IncludeTestProject(std::string_view code_text,
                     std::string_view include_file_basename,
                     std::string_view include_text,
                     const std::vector<std::string> &include_paths = {})
      : SimpleTestProject(code_text, include_paths),
        include_file_(
            include_text, temp_dir_,
            [this](
                std::string_view full_file_name) -> const VerilogSourceFile * {
              /* VerilogProject base class is already fully initialized */
              return *OpenIncludedFile(verible::file::Basename(full_file_name));
            },
            include_file_basename) {}

  using SimpleTestProject::Errors;
  using SimpleTestProject::ExpectedFileListData;
  using SimpleTestProject::Extract;
  using SimpleTestProject::XUnit;

  const TestFileEntry &IncludeFile() const { return include_file_; }

 private:
  // The one and only one included file in the test.
  TestFileEntry include_file_;
};

TEST(FactsTreeExtractor, EqualOperatorTest) {
  constexpr std::string_view code_text = "some other code";
  constexpr std::string_view file_name = "verilog.v";
  constexpr std::string_view file_name_other = "other.v";

  const IndexingFactNode expected(D{
      IndexingFactType::kFile,
      Anchor(file_name),
  });

  // same as expected
  const IndexingFactNode tree(D{
      IndexingFactType::kFile,
      Anchor(file_name),
  });

  // different filename
  const IndexingFactNode tree2(D{
      IndexingFactType::kFile,
      Anchor(file_name_other),
  });

  // different number of anchors
  const IndexingFactNode tree3(D{
      IndexingFactType::kFile,
      Anchor(file_name),
      Anchor(file_name),
  });

  // completely different string_view ranges
  const IndexingFactNode tree4(
      D{
          IndexingFactType::kFile,
          Anchor(file_name),
      },
      T(D{
          IndexingFactType::kModule,
          Anchor(std::string_view("foo")),
          Anchor(std::string_view("foo")),
      }));

  const auto P = [&code_text](const IndexingFactNode &n) {
    return PrintableIndexingFactNode(n, code_text);
  };
  {
    const auto result_pair = DeepEqual(tree, expected);
    EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
    EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
  }
  {
    const auto result_pair2 = DeepEqual(tree2, expected);
    EXPECT_NE(result_pair2.left, nullptr) << P(*result_pair2.left);
    EXPECT_NE(result_pair2.right, nullptr) << P(*result_pair2.right);
  }
  {
    const auto result_pair3 = DeepEqual(tree3, expected);
    EXPECT_NE(result_pair3.left, nullptr) << P(*result_pair3.left);
    EXPECT_NE(result_pair3.right, nullptr) << P(*result_pair3.right);
  }
  {
    const auto result_pair4 = DeepEqual(tree4, expected);
    EXPECT_NE(result_pair4.left, nullptr) << P(*result_pair4.left);
    EXPECT_NE(result_pair4.right, nullptr) << P(*result_pair4.right);
  }
}

TEST(FactsTreeExtractor, EmptyCSTTest) {
  constexpr std::string_view code_text;

  SimpleTestProject project(code_text);

  const IndexingFactNode expected(     //
      project.ExpectedFileListData(),  //
      T(project.XUnit().ExpectedFileData()));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ParseErrorTest) {
  // These inputs are lexically or syntactically invalid.
  constexpr std::string_view code_texts[] = {
      "9badid foo;\n",      // lexical error
      "final v;\n",         // syntax error
      "module unfinished",  // syntax error
  };

  for (const auto &code_text : code_texts) {
    SimpleTestProject project(code_text);
    const auto facts_tree = project.Extract();
    const std::vector<absl::Status> errors(project.Errors());
    EXPECT_FALSE(errors.empty()) << "code\n" << code_text;
  }
}

TEST(FactsTreeExtractor, EmptyModuleTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ", {kTag, "foo"}, ";\n endmodule: ", {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(D{
            IndexingFactType::kModule,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        })));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, PropagatedUserDefinedTypeInModulePort) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{
      "module ",
      {kTag, "foo"},
      "(",
      {kTag, "My_type"},
      " ",
      {kTag, "x"},
      ", ",
      {kTag, "y"},
      ");\n endmodule",
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to My_type.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                }),
                // refers to y.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, OneLocalNetTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase{{
      "module ",
      {kTag, "bar"},
      ";\n"
      "wire ",
      {kTag, "w"},
      ";\n"
      "tri ",
      {kTag, "`x"},
      ";\n"
      "endmodule",
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module bar.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to w.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to `x
            // TODO(minatoma): suppress declarations that use macro
            // identifiers because they evaluate to something unknown.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, OneModuleInstanceTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "bar"},
                                                        ";\n endmodule: ",
                                                        {kTag, "bar"},
                                                        "\nmodule ",
                                                        {kTag, "foo"},
                                                        ";\n ",
                                                        {kTag, "bar"},
                                                        " ",
                                                        {kTag, "b1"},
                                                        "();\n endmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module bar.
        T(D{
            IndexingFactType::kModule,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        }),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            },
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                },
                // refers to bar b1().
                T(D{
                    IndexingFactType::kModuleInstance,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, TwoModuleInstanceTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "bar"},
                                                        ";\n endmodule: ",
                                                        {kTag, "bar"},
                                                        "\nmodule ",
                                                        {kTag, "foo"},
                                                        "; ",
                                                        {kTag, "bar"},
                                                        " ",
                                                        {kTag, "b1"},
                                                        "();\n",
                                                        {kTag, "bar"},
                                                        " ",
                                                        {kTag, "b2"},
                                                        "();\nendmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module bar.
        T(D{
            IndexingFactType::kModule,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        }),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                Anchor(kTestCase.expected_tokens[15], kTestCase.code),
            },
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                },
                // refers to bar b1().
                T(D{
                    IndexingFactType::kModuleInstance,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                })),
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                },
                // refers to bar b2().
                T(D{
                    IndexingFactType::kModuleInstance,
                    Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}  // namespace

TEST(FactsTreeExtractor, MultipleModuleInstancesInTheSameDeclarationTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "bar"},
                                                        ";\nendmodule: ",
                                                        {kTag, "bar"},
                                                        "\nmodule ",
                                                        {kTag, "foo"},
                                                        ";\n",
                                                        {kTag, "bar"},
                                                        " ",
                                                        {kTag, "b1"},
                                                        "(), ",
                                                        {kTag, "b2"},
                                                        "();\nendmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module bar.
        T(D{
            IndexingFactType::kModule,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        }),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            },
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                },
                // refers to b1().
                T(D{
                    IndexingFactType::kModuleInstance,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                }),
                // refers to bar b2().
                T(D{
                    IndexingFactType::kModuleInstance,
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}  // namespace

TEST(FactsTreeExtractor, ModuleWithPortsTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "foo"},
                                                        "(input ",
                                                        {kTag, "a"},
                                                        ", output ",
                                                        {kTag, "b"},
                                                        ");\nendmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            },
            // refers to input a.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to output b.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ModuleDimensionTypePortsTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",
       {kTag, "foo"},
       "(input ",
       {kTag, "a"},
       ", output [",
       {kTag, "x"},
       ":",
       {kTag, "y"},
       "] ",
       {kTag, "b"},
       " [",
       {kTag, "x"},
       ":",
       {kTag, "y"},
       "]);\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to input a.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to output b.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            }),
            // refers to input x.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to input y.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),

            // refers to input x.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            }),
            // refers to input y.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ModuleWithPortsNonANSIStyleTest) {
  constexpr int kTag = 1;  // value doesn't matter

  // Normally, tools will reject non-ANSI port declarations that are missing
  // their full definitions inside the body like "input a", but here we don't
  // care and are just checking for references, even if they are dangling.
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "foo"},
                                                        "(",
                                                        {kTag, "a"},
                                                        ", ",
                                                        {kTag, "b"},
                                                        ", input wire ",
                                                        {kTag, "z"},
                                                        ", ",
                                                        {kTag, "h"},
                                                        ");\nendmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            },
            // refers to  a.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to  b.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to input z.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            // refers to h.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ClassParams) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "my_class"},
       " #(parameter ",
       {kTag, "x"},
       " = 4, ",
       {kTag, "y"},
       " = 4); endclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class my_class.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to parameter x
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to y
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, QualifiedVariableType) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",
       {kTag, "m"},
       ";\n ",
       {kTag, "pkg"},
       "::",
       {kTag, "X"},
       " ",
       {kTag, "y"},
       ";\n ",
       {kTag, "pkg"},
       "::",
       {kTag, "H"},
       " ",
       {kTag, "j"},
       " = new();\nendmodule"}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module m.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to pkg::X
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to y
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                })),
            // refers to pkg::H
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                },
                // refers to j
                T(D{
                    IndexingFactType::kClassInstance,
                    Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ClassTypeParams) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "my_class"},
       " #(type ",
       {kTag, "x"},
       " = int, ",
       {kTag, "y"},
       " = int); endclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class my_class.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to parameter x
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to y
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ModuleInstanceWithActualNamedPorts) {
  constexpr int kTag = 1;  // value doesn't matter

  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",          // 0
       {kTag, "foo"},      // 1
       "(input ",          // 2
       {kTag, "a"},        // 3
       ", input ",         // 4
       {kTag, "b"},        // 5
       ", input wire ",    // 6
       {kTag, "z"},        // 7
       ", output ",        // 8
       {kTag, "h"},        // 9
       ");\nendmodule: ",  // 10
       {kTag, "foo"},      // 11
       "\nmodule ",        // 12
       {kTag, "bar"},      // 13
       "(input ",          // 14
       {kTag, "a"},        // 15
       ", ",               // 16
       {kTag, "b"},        // 17
       ", ",               // 18
       {kTag, "c"},        // 19
       ", ",               // 20
       {kTag, "h"},        // 21
       ");\n",             // 22
       {kTag, "foo"},      // 23
       " ",                // 24
       {kTag, "f1"},       // 25
       "(.",               // 26
       {kTag, "a"},        // 27
       "(",                // 28
       {kTag, "a"},        // 29
       "), .",             // 30
       {kTag, "b"},        // 31
       "(",                // 32
       {kTag, "b"},        // 33
       "), .",             // 34
       {kTag, "z"},        // 35
       "(",                // 36
       {kTag, "c"},        // 37
       "), .",             // 38
       {kTag, "h"},        // 39
       ");\nendmodule"}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            },
            // refers to  a.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to  b.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to input z.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            // refers to h.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            })),
        // refers to module bar.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            },
            // refers to input a.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[15], kTestCase.code),
            }),
            // refers to b.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[17], kTestCase.code),
            }),
            // refers to c.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[19], kTestCase.code),
            }),
            // refers to h.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[21], kTestCase.code),
            }),
            // refers to foo.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[23], kTestCase.code),
                },
                // refers to f1(.a(a), .b(b), .z(c), .h).
                T(
                    D{
                        IndexingFactType::kModuleInstance,
                        Anchor(kTestCase.expected_tokens[25], kTestCase.code),
                    },
                    // refers to .a
                    T(
                        D{
                            IndexingFactType::kModuleNamedPort,
                            Anchor(kTestCase.expected_tokens[27],
                                   kTestCase.code),
                        },
                        // refers to a
                        T(D{
                            IndexingFactType::kVariableReference,
                            Anchor(kTestCase.expected_tokens[29],
                                   kTestCase.code),
                        })),
                    // refers to .b
                    T(
                        D{
                            IndexingFactType::kModuleNamedPort,
                            Anchor(kTestCase.expected_tokens[31],
                                   kTestCase.code),
                        },
                        // refers to b
                        T(D{
                            IndexingFactType::kVariableReference,
                            Anchor(kTestCase.expected_tokens[33],
                                   kTestCase.code),
                        })),
                    // refers to .z
                    T(
                        D{
                            IndexingFactType::kModuleNamedPort,
                            Anchor(kTestCase.expected_tokens[35],
                                   kTestCase.code),
                        },
                        // refers to c
                        T(D{
                            IndexingFactType::kVariableReference,
                            Anchor(kTestCase.expected_tokens[37],
                                   kTestCase.code),
                        })),
                    // refers to .h
                    T(D{
                        IndexingFactType::kModuleNamedPort,
                        Anchor(kTestCase.expected_tokens[39], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ModuleInstanceWitStarExpandedPorts) {
  constexpr int kTag = 1;  // value doesn't matter

  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",          // 00
       {kTag, "foo"},      // 01
       "(input ",          // 02
       {kTag, "a"},        // 03
       ");\nendmodule\n",  // 04
       "\nmodule ",        // 05
       {kTag, "bar"},      // 06
       "(input ",          // 07
       {kTag, "a"},        // 08
       ");\n",             // 09
       {kTag, "foo"},      // 10
       " ",                // 11
       {kTag, "f1"},       // 12
       "(.",               // 13
       {kTag, "*"},        // 14
       ");\nendmodule"}};

  SimpleTestProject project(kTestCase.code);
  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to  a.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            })),
        // refers to module bar.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[6], kTestCase.code),
            },
            // refers to input a.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[8], kTestCase.code),
            }),
            // refers to foo.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[10], kTestCase.code),
                },
                // refers to f1(.*)
                T(D{
                    IndexingFactType::kModuleInstance,
                    Anchor(kTestCase.expected_tokens[12], kTestCase.code),
                }  // The .* is not recorded as named module port.
                  )))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ModuleWithPortsDataTypeForwarding) {
  constexpr int kTag = 1;  // value doesn't matter

  // Normally, tools will reject non-ANSI port declarations that are missing
  // their full definitions inside the body like "input a", but here we don't
  // care and are just checking for references, even if they are dangling.
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "foo"},
                                                        "(input wire ",
                                                        {kTag, "a"},
                                                        ", ",
                                                        {kTag, "b"},
                                                        ", output wire ",
                                                        {kTag, "z"},
                                                        ", ",
                                                        {kTag, "h"},
                                                        ");\nendmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            },
            // refers to  a.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to  b.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to input z.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            // refers to h.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, PrimitiveTypeExtraction) {
  constexpr int kTag = 1;  // value doesn't matter

  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg"},
       ";\n int ",
       {kTag, "x"},
       ", ",
       {kTag, "y"},
       ";\nlogic ",
       {kTag, "l1"},
       ", ",
       {kTag, "l2"},
       ";\nbit ",
       {kTag, "b1"},
       ", ",
       {kTag, "b2"},
       ";\nstring ",
       {kTag, "s1"},
       ", ",
       {kTag, "s2"},
       ";\nendpackage\nclass ",
       {kTag, "cla"},
       ";\n int ",
       {kTag, "x"},
       ", ",
       {kTag, "y"},
       ";\nlogic ",
       {kTag, "l1"},
       ", ",
       {kTag, "l2"},
       ";\nbit ",
       {kTag, "b1"},
       ", ",
       {kTag, "b2"},
       ";\nstring ",
       {kTag, "s1"},
       ", ",
       {kTag, "s2"},
       ";\nendclass\nfunction int ",
       {kTag, "fun"},
       "();\n int ",
       {kTag, "x"},
       " = 5, ",
       {kTag, "y"},
       " = ",
       {kTag, "my_fun"},
       "(",
       {kTag, "o"},
       ", ",
       {kTag, "l"},
       ");\nlogic ",
       {kTag, "l1"},
       ", ",
       {kTag, "l2"},
       ";\nbit ",
       {kTag, "b1"},
       ", ",
       {kTag, "b2"},
       ";\nstring ",
       {kTag, "s1"},
       ", ",
       {kTag, "s2"},
       ";\nreturn ",
       {kTag, "x"},
       ";\nendfunction"}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to x;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to y;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to l1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            // refers to l2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            }),
            // refers to b1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            }),
            // refers to b2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            }),
            // refers to s1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[15], kTestCase.code),
            }),
            // refers to s2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[17], kTestCase.code),
            })),
        // refers to class cla.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[19], kTestCase.code),
            },
            // refers to x;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[21], kTestCase.code),
            }),
            // refers to y;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[23], kTestCase.code),
            }),
            // refers to l1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[25], kTestCase.code),
            }),
            // refers to l2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[27], kTestCase.code),
            }),
            // refers to b1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[29], kTestCase.code),
            }),
            // refers to b2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[31], kTestCase.code),
            }),
            // refers to s1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[33], kTestCase.code),
            }),
            // refers to s2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[35], kTestCase.code),
            })),
        // refers to function fun.
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[37], kTestCase.code),
            },
            // refers to x;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[39], kTestCase.code),
            }),
            // refers to y;
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[41], kTestCase.code),
                },
                // refers to my_fun;
                T(
                    D{
                        IndexingFactType::kFunctionCall,
                        Anchor(kTestCase.expected_tokens[43], kTestCase.code),
                    },
                    // refers to o;
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[45], kTestCase.code),
                    }),
                    // refers to l;
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[47], kTestCase.code),
                    }))),
            // refers to l1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[49], kTestCase.code),
            }),
            // refers to l2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[51], kTestCase.code),
            }),
            // refers to b1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[53], kTestCase.code),
            }),
            // refers to b2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[55], kTestCase.code),
            }),
            // refers to s1;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[57], kTestCase.code),
            }),
            // refers to s2;
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[59], kTestCase.code),
            }),
            // refers to return x;
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[61], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, MultiSignalDeclaration) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "foo"},
                                                        "(",
                                                        "input ",
                                                        {kTag, "in"},
                                                        ");\n",
                                                        "input ",
                                                        {kTag, "x"},
                                                        ", ",
                                                        {kTag, "y"},
                                                        ";\noutput ",
                                                        {kTag, "z"},
                                                        ";\nendmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            },
            // refers to input in.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[4], kTestCase.code),
            }),
            // refers to output x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            // refers to output y
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            }),
            // refers to output z.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ModuleInstanceWithPortsTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "bar"},
                                                        "(input ",
                                                        {kTag, "x"},
                                                        ", output ",
                                                        {kTag, "y"},
                                                        ");\nendmodule: ",
                                                        {kTag, "bar"},
                                                        "\nmodule ",
                                                        {kTag, "foo"},
                                                        "(input ",
                                                        {kTag, "x"},
                                                        ", output ",
                                                        {kTag, "y"},
                                                        ");\n ",
                                                        {kTag, "bar"},
                                                        " ",
                                                        {kTag, "b1"},
                                                        "(",
                                                        {kTag, "x"},
                                                        ", ",
                                                        {kTag, "y"},
                                                        ");\nendmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module bar.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            },
            // refers to input x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to output y.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            })),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                Anchor(kTestCase.expected_tokens[23], kTestCase.code),
            },
            // refers to input x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            }),
            // refers to output y.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            }),
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[15], kTestCase.code),
                },
                // refers to b1(x, y).
                T(
                    D{
                        IndexingFactType::kModuleInstance,
                        Anchor(kTestCase.expected_tokens[17], kTestCase.code),
                    },
                    // refers to x
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[19], kTestCase.code),
                    }),
                    // refers to y
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[21], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}  // namespace

TEST(FactsTreeExtractor, WireTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "foo"},
                                                        "();\nwire ",
                                                        {kTag, "a"},
                                                        ";\nendmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            },
            // refers to "wire a"
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ClassTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ", {kTag, "foo"}, ";\nendclass: ", {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class foo
        T(D{
            IndexingFactType::kClass,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        })));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, CLassWithinModuleTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"module ",
                                                        {kTag, "m"},
                                                        "();\nclass ",
                                                        {kTag, "foo"},
                                                        ";\nendclass:",
                                                        {kTag, "foo"},
                                                        ";\nendmodule: ",
                                                        {kTag, "m"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module foo
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            },
            // refers to "class foo"
            T(D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, NestedClassTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{
      "class ",
      {kTag, "foo"},
      ";\nclass ",
      {kTag, "bar"},
      ";\nendclass: ",
      {kTag, "bar"},
      "\nendclass: ",
      {kTag, "foo"},
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class foo
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            },
            // refers to class bar
            T(D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, OneClassInstanceTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{"class ",
                                                        {kTag, "bar"},
                                                        ";\n endclass: ",
                                                        {kTag, "bar"},
                                                        "\nmodule ",
                                                        {kTag, "foo"},
                                                        "();\n ",
                                                        {kTag, "bar"},
                                                        " ",
                                                        {kTag, "b1"},
                                                        "= new(), ",
                                                        {kTag, "b2"},
                                                        " = new(",
                                                        {kTag, "x"},
                                                        ", ",
                                                        {kTag, "y"},
                                                        ");\n endmodule: ",
                                                        {kTag, "foo"}}};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class bar.
        T(D{
            IndexingFactType::kClass,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        }),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                Anchor(kTestCase.expected_tokens[17], kTestCase.code),
            },
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                },
                // refers to b1.
                T(D{
                    IndexingFactType::kClassInstance,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                }),
                // refers to b2.
                T(
                    D{
                        IndexingFactType::kClassInstance,
                        Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                    },
                    // refers to x.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                    }),
                    // refers to y.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[15], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ClassMemberAccess) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{
      "class ",          {kTag, "inner"},
      ";\n static int ", {kTag, "x"},
      ";\nendclass\n",   "class ",
      {kTag, "bar"},     ";\n static ",
      {kTag, "inner"},   " ",
      {kTag, "in1"},     " = new();\nendclass: ",
      {kTag, "bar"},     "\nmodule ",
      {kTag, "foo"},     "();\n ",
      {kTag, "bar"},     " ",
      {kTag, "b1"},      "= new();\n initial $display(",
      {kTag, "bar"},     "::",
      {kTag, "in"},      "::",
      {kTag, "x"},       ");\nendmodule: ",
      {kTag, "foo"},
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class inner.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to int x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            })),
        // refers to class bar.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[6], kTestCase.code),
                Anchor(kTestCase.expected_tokens[12], kTestCase.code),
            },
            // refers to inner in1.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[8], kTestCase.code),
                },
                // refers to in1.
                T(D{
                    IndexingFactType::kClassInstance,
                    Anchor(kTestCase.expected_tokens[10], kTestCase.code),
                }))),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[14], kTestCase.code),
                Anchor(kTestCase.expected_tokens[26], kTestCase.code),
            },
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[16], kTestCase.code),
                },
                // refers to b1.
                T(D{
                    IndexingFactType::kClassInstance,
                    Anchor(kTestCase.expected_tokens[18], kTestCase.code),
                })),
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                // refers to bar::in::x.
                T(D{
                    IndexingFactType::kMemberReference,
                    Anchor(kTestCase.expected_tokens[20], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[22], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[24], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, FunctionAndTaskDeclarationNoArgs) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{
      "function int ",
      {kTag, "foo"},
      "();",
      ";\nendfunction ",
      "task ",
      {kTag, "bar"},
      "();",
      ";\nendtask ",
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function foo
        T(D{
            IndexingFactType::kFunctionOrTask,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
        }),
        // refers to task bar
        T(D{
            IndexingFactType::kFunctionOrTask,
            Anchor(kTestCase.expected_tokens[5], kTestCase.code),
        })));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, FunctionAndTaskDeclarationWithArgs) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{
      "function int ", {kTag, "foo"},  "(int ", {kTag, "arg1"},
      ", input ",      {kTag, "arg2"}, ", ",    {kTag, "arg3"},
      ", bit ",        {kTag, "arg4"}, ");",    ";\nendfunction ",
      "task ",         {kTag, "bar"},  "(int ", {kTag, "arg1"},
      ", input ",      {kTag, "arg2"}, ", ",    {kTag, "arg3"},
      ", bit ",        {kTag, "arg4"}, ");",    ";\nendtask ",
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function foo
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            })),
        // refers to task bar
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            },
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[15], kTestCase.code),
            }),
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[17], kTestCase.code),
            }),
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[19], kTestCase.code),
            }),
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[21], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ClassMember) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{
      "module ",
      {kTag, "m"},
      "();\ninitial begin\n$display(",
      {kTag, "my_class"},
      ".",
      {kTag, "x"},
      ");\n$display(",
      {kTag, "my_class"},
      ".",
      {kTag, "instance1"},
      ".",
      {kTag, "y"},
      ");\nend\nendmodule",
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module m
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                // refers to my_class.x
                T(D{
                    IndexingFactType::kMemberReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                }),
                // refers to my_class.instance1.x
                T(D{
                    IndexingFactType::kMemberReference,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, FunctionAndTaskCallNoArgs) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{
      "function int ",
      {kTag, "foo"},
      "();",
      ";\nendfunction\n ",
      "task ",
      {kTag, "bar"},
      "();",
      ";\nendtask ",
      "\nmodule ",
      {kTag, "m"},
      "();\ninitial begin\n",
      {kTag, "foo"},
      "();\n",
      {kTag, "bar"},
      "();\nend\nendmodule: ",
      {kTag, "m"},
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function foo
        T(D{
            IndexingFactType::kFunctionOrTask,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
        }),
        // refers to task bar
        T(D{
            IndexingFactType::kFunctionOrTask,
            Anchor(kTestCase.expected_tokens[5], kTestCase.code),
        }),
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                Anchor(kTestCase.expected_tokens[15], kTestCase.code),
            },
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                T(D{
                    IndexingFactType::kFunctionCall,
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                }),
                T(D{
                    IndexingFactType::kFunctionCall,
                    Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, FunctionClassCall) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {{
      "class ",
      {kTag, "inner"},
      ";\n static function int ",
      {kTag, "my_fun"},
      "();\nreturn 1;\nendfunction\nfunction int ",
      {kTag, "fun_2"},
      "(int ",
      {kTag, "x"},
      ", int ",
      {kTag, "y"},
      ");\nreturn ",
      {kTag, "x"},
      " + ",
      {kTag, "y"},
      ";\nendfunction\nendclass\n",
      "class ",
      {kTag, "bar"},
      ";\n static ",
      {kTag, "inner"},
      " ",
      {kTag, "in1"},
      " = new();\nendclass: ",
      {kTag, "bar"},
      "\nmodule ",
      {kTag, "foo"},
      "();\n ",
      {kTag, "bar"},
      " ",
      {kTag, "b1"},
      "= new();\n initial $display(",
      {kTag, "bar"},
      "::",
      {kTag, "in"},
      "::",
      {kTag, "my_fun"},
      "());\ninitial $display(",
      {kTag, "bar"},
      "::",
      {kTag, "in"},
      ".",
      {kTag, "my_fun"},
      "());\n",
      {kTag, "inner"},
      " ",
      {kTag, "in1"},
      " = new();\nint ",
      {kTag, "x"},
      ", ",
      {kTag, "y"},
      ";\ninitial $display(",
      {kTag, "in1"},
      ".",
      {kTag, "fun_2"},
      "(",
      {kTag, "x"},
      ", ",
      {kTag, "y"},
      "));\nendmodule",
  }};

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class inner.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to function my_fun.
            T(D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to function fun_2.
            T(
                D{
                    IndexingFactType::kFunctionOrTask,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x arg in fun_2.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                }),
                // refers to y arg in fun_2.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                }),
                // refers to x.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                }),
                // refers to x.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                }))),
        // refers to class bar.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[16], kTestCase.code),
                Anchor(kTestCase.expected_tokens[22], kTestCase.code),
            },
            // refers to inner in1.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[18], kTestCase.code),
                },
                // refers to in1.
                T(D{
                    IndexingFactType::kClassInstance,
                    Anchor(kTestCase.expected_tokens[20], kTestCase.code),
                }))),
        // refers to module foo.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[24], kTestCase.code),
            },
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[26], kTestCase.code),
                },
                // refers to b1.
                T(D{
                    IndexingFactType::kClassInstance,
                    Anchor(kTestCase.expected_tokens[28], kTestCase.code),
                })),
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                // refers to bar::in::my_fun().
                T(D{
                    IndexingFactType::kFunctionCall,
                    Anchor(kTestCase.expected_tokens[30], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[32], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[34], kTestCase.code),
                })),
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-1"),
                },
                // refers to bar::in.my_fun().
                T(D{
                    IndexingFactType::kFunctionCall,
                    Anchor(kTestCase.expected_tokens[36], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[38], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[40], kTestCase.code),
                })),
            // refers to inner in1.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[42], kTestCase.code),
                },
                // refers to in1.
                T(D{
                    IndexingFactType::kClassInstance,
                    Anchor(kTestCase.expected_tokens[44], kTestCase.code),
                })),
            // refers to int x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[46], kTestCase.code),
            }),
            // refers to int y.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[48], kTestCase.code),
            }),
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-2"),
                },
                // refers to in1.my_fun().
                T(
                    D{
                        IndexingFactType::kFunctionCall,
                        Anchor(kTestCase.expected_tokens[50], kTestCase.code),
                        Anchor(kTestCase.expected_tokens[52], kTestCase.code),
                    },
                    // refers to x.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[54], kTestCase.code),
                    }),
                    // refers to y.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[56], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ThisAsFunctionCall) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase{
      // Note: this test case is also significant in that it exercises
      // short-string-optimization, where by sufficiently short strings live
      // directly in the object, rather than in heap memory.
      {
          {kTag, "r"},
          "=this();",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to r.
        T(D{
            IndexingFactType::kVariableDefinition,
            Anchor(kTestCase.expected_tokens[0], kTestCase.code),
        })));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, MacroDefinitionTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "`define ",
          {kTag, "PRINT_STRING"},
          "(",
          {kTag, "str1"},
          ") $display(\"%s\\n\", str1)\n",
          "`define ",
          {kTag, "PRINT_3_STRING"},
          "(",
          {kTag, "str1"},
          ", ",
          {kTag, "str2"},
          ", ",
          {kTag, "str3"},
          ")",
          R"( \
    `PRINT_STRING(str1); \
    `PRINT_STRING(str2); \
    `PRINT_STRING(str3);)",
          "\n`define ",
          {kTag, "TEN"},
          " 10",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to macro PRINT_STRING.
        T(
            D{
                IndexingFactType::kMacro,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to str1 arg in PRINT_STRING.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            })),
        // refers to macro PRINT_3_STRING.
        T(
            D{
                IndexingFactType::kMacro,
                Anchor(kTestCase.expected_tokens[6], kTestCase.code),
            },
            // refers to str1 arg in PRINT_3_STRING.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[8], kTestCase.code),
            }),
            // refers to str2 arg in PRINT_3_STRING.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[10], kTestCase.code),
            }),
            // refers to str3 arg in PRINT_3_STRING.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[12], kTestCase.code),
            })),
        // refers to macro TEN.
        T(D{
            IndexingFactType::kMacro,
            Anchor(kTestCase.expected_tokens[16], kTestCase.code),
        })));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, MacroCallTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      "`define ",
      {kTag, "PRINT_STRING"},
      "(",
      {kTag, "str1"},
      ") $display(\"%s\\n\", str1)\n",
      "`define ",
      {kTag, "PRINT_3_STRING"},
      "(",
      {kTag, "str1"},
      ", ",
      {kTag, "str2"},
      ", ",
      {kTag, "str3"},
      ")",
      R"( \
    `PRINT_STRING(str1); \
    `PRINT_STRING(str2); \
    `PRINT_STRING(str3);)",
      "\n`define ",
      {kTag, "TEN"},
      " 10\n",
      "\n`define ",
      {kTag, "NUM"},
      "(",
      {kTag, "i"},
      ") i\n",
      "module ",
      {kTag, "macro"},
      ";\ninitial begin\n`",
      {kTag, "PRINT_3_STRINGS"},
      "(\"Grand\", \"Tour\", \"S4\");\n",
      "$display(\"%d\\n\", `",
      {kTag, "TEN"},
      ");\n",
      "$display(\"%d\\n\", `",
      {kTag, "NUM"},
      "(`",
      {kTag, "TEN"},
      "));\n",
      "parameter int ",
      {kTag, "x"},
      " = `",
      {kTag, "TEN"},
      ";\n"
      "end\nendmodule",
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to macro PRINT_STRING.
        T(
            D{
                IndexingFactType::kMacro,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to str1 in PRINT_STRING.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            })),
        // refers to macro PRINT_3_STRING.
        T(
            D{
                IndexingFactType::kMacro,
                Anchor(kTestCase.expected_tokens[6], kTestCase.code),
            },
            // refers to str1 in PRINT_3_STRING.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[8], kTestCase.code),
            }),
            // refers to str2 in PRINT_3_STRING.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[10], kTestCase.code),
            }),
            // refers to str3 in PRINT_3_STRING.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[12], kTestCase.code),
            })),
        // refers to macro TEN.
        T(D{
            IndexingFactType::kMacro,
            Anchor(kTestCase.expected_tokens[16], kTestCase.code),
        }),
        // refers to macro NUM.
        T(
            D{
                IndexingFactType::kMacro,
                Anchor(kTestCase.expected_tokens[19], kTestCase.code),
            },
            // refers to i in macro NUM.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[21], kTestCase.code),
            })),
        // refers to module macro.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[24], kTestCase.code),
            },
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                // refers to macro call PRINT_3_STRINGS.
                T(D{
                    IndexingFactType::kMacroCall,
                    Anchor(kTestCase.expected_tokens[26], kTestCase.code),
                }),
                // refers to macro call TEN.
                T(D{
                    IndexingFactType::kMacroCall,
                    Anchor(kTestCase.expected_tokens[29], kTestCase.code),
                }),
                // refers to macro call NUM.
                T(
                    D{
                        IndexingFactType::kMacroCall,
                        Anchor(kTestCase.expected_tokens[32], kTestCase.code),
                    },  // refers to macro call TEN.
                    T(D{
                        IndexingFactType::kMacroCall,
                        Anchor(kTestCase.expected_tokens[34], kTestCase.code),
                    })),
                // refers to parm x
                T(
                    D{
                        IndexingFactType::kParamDeclaration,
                        Anchor(kTestCase.expected_tokens[37], kTestCase.code),
                    },
                    // refers to macro call TEN.
                    T(D{
                        IndexingFactType::kMacroCall,
                        Anchor(kTestCase.expected_tokens[39], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(PackageImportTest, PackageAndImportedItemName) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg1"},
       ";\nendpackage\npackage ",
       {kTag, "pkg"},
       ";\nclass ",
       {kTag, "my_class"},
       ";\nendclass\nfunction ",
       {kTag, "my_function"},
       "();\nendfunction\nendpackage\nmodule ",
       {kTag, "m"},
       "();\nimport ",
       {kTag, "pkg1"},
       "::*;\nimport ",
       {kTag, "pkg"},
       "::",
       {kTag, "my_function"},
       ";\nimport ",
       {kTag, "pkg"},
       "::",
       {kTag, "my_class"},
       ";\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg1.
        T(D{
            IndexingFactType::kPackage,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
        }),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            },
            // refers to class my_class.
            T(D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to function my_function.
            T(D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            })),
        // refers to module m..
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            },
            // refers to import pkg1::*.
            T(D{
                IndexingFactType::kPackageImport,
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            }),
            // refers to import pkg::my_function.
            T(D{
                IndexingFactType::kPackageImport,
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                Anchor(kTestCase.expected_tokens[15], kTestCase.code),
            }),
            // refers to import pkg::my_class.
            T(D{
                IndexingFactType::kPackageImport,
                Anchor(kTestCase.expected_tokens[17], kTestCase.code),
                Anchor(kTestCase.expected_tokens[19], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(PackageImportTest, PackageDirectMemberReference) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg"},
       ";\nclass ",
       {kTag, "my_class"},
       ";\nendclass\nwire ",
       {kTag, "x"},
       ";\nendpackage\nmodule ",
       {kTag, "m"},
       "();\n",
       "initial $display(",
       {kTag, "pkg"},
       "::",
       {kTag, "x"},
       ");\n",
       ";\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to class my_class.
            T(D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to wire x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            })),
        // refers to module m..
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            },
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                // refers to $display(pkg::x).
                T(D{
                    IndexingFactType::kMemberReference,
                    Anchor(kTestCase.expected_tokens[10], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[12], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(PackageImportTest, ClassInstanceWithMultiParams) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "function ",
          {kTag, "m"},
          "();\n",
          {kTag, "foo"},
          "#(.",
          {kTag, "A"},
          "(",
          {kTag, "var1"},
          "), .",
          {kTag, "B"},
          "(",
          {kTag, "var2"},
          "))::",
          {kTag, "barc"},
          "#(.",
          {kTag, "X"},
          "(",
          {kTag, "var1"},
          "), .",
          {kTag, "W"},
          "(",
          {kTag, "var2"},
          "))::",
          {kTag, "get2"},
          "();\nendfunction",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function m.
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers foo(.A, .B).
            T(
                D{
                    IndexingFactType::kMemberReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers A.
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                    },
                    // refers var1.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                    })),
                // refers B.
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                    },
                    // refers var2.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                    }))),
            // refers barc(.X, .W).
            T(
                D{
                    IndexingFactType::kMemberReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                },
                // refers X.
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[15], kTestCase.code),
                    },
                    // refers var1.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[17], kTestCase.code),
                    })),
                // refers W.
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[19], kTestCase.code),
                    },
                    // refers var2.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[21], kTestCase.code),
                    }))),
            // refers var2.
            T(D{
                IndexingFactType::kFunctionCall,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                Anchor(kTestCase.expected_tokens[23], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(PackageImportTest, UserDefinedType) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "Stack"},
       ";\ntypedef int ",
       {kTag, "some_type"},
       ";\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class stack.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to some_type.
            T(D{
                IndexingFactType::kTypeDeclaration,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(PackageImportTest, SelectVariableDimension) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "task ",
          {kTag, "t"},
          "(int ",
          {kTag, "y"},
          ");\n@",
          {kTag, "x"},
          "[",
          {kTag, "y"},
          "];\nendtask",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to task t.
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to y.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to x.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to y.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(PackageImportTest, ClassParameterType) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "Stack"},
       " #(parameter type ",
       {kTag, "T"},
       "=int);\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class stack.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to T.
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(PackageImportTest, ClassInstanceWithParams) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "module ",
          {kTag, "m"},
          "();\n",
          {kTag, "clt"},
          "#(",
          {kTag, "x"},
          ") ",
          {kTag, "v1"},
          " = new();\nendmodule",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module m.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to clt#(x) v1.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                }),
                // refers to v1.
                T(D{
                    IndexingFactType::kClassInstance,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(PackageImportTest, PackageTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "package ",
          {kTag, "pkg"},
          ";\nendpackage: ",
          {kTag, "pkg"},
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(D{
            IndexingFactType::kPackage,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        })));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ForLoopInitializations) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"function int ",
       {kTag, "foo"},
       "();\n for (int ",
       {kTag, "i"},
       " = 0, bit ",
       {kTag, "j"},
       " = 0, bit[",
       {kTag, "l"},
       ":",
       {kTag, "r"},
       "] ",
       {kTag, "tm"},
       " = 0; ",
       {kTag, "i"},
       " < 50; ",
       {kTag, "i"},
       "++) begin\n",
       {kTag, "x"},
       "+=",
       {kTag, "i"},
       ";\nend\nreturn ",
       {kTag, "x"},
       ";\n\nendfunction "},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function foo
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                // refers to i
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                }),
                // refers to j
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                }),
                // refers to tm
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                }),
                // refers to l
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                }),
                // refers to r
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                }),
                // refers to i
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                }),
                // refers to i
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[15], kTestCase.code),
                }),
                // refers to x
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[17], kTestCase.code),
                }),
                // refers to i
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[19], kTestCase.code),
                })),
            // refers to x
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[21], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ClassExtends) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "X"},
       " extends ",
       {kTag, "Y"},
       ";\nendclass\nclass ",
       {kTag, "H"},
       " extends ",
       {kTag, "G"},
       "::",
       {kTag, "K"},
       ";\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class X.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to Y.
            T(D{
                IndexingFactType::kExtends,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            })),
        // refers to H.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            },
            // refers to G::K.
            T(D{
                IndexingFactType::kExtends,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            }))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ParameterExtraction) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",
       {kTag, "m"},
       "#(parameter ",
       {kTag, "x"},
       " = 1, parameter ",
       {kTag, "y"},
       " = 2) (input ",
       {kTag, "z"},
       ");\n ",
       {kTag, "bar"},
       " #(.",
       {kTag, "p1"},
       "(",
       {kTag, "x"},
       "), .",
       {kTag, "p2"},
       "(",
       {kTag, "y"},
       ")) ",
       {kTag, "b1"},
       "(",
       {kTag, "z"},
       ");\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module m.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to module parameter x.
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to class parameter y.
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to class input x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                },
                // refers to .p1(x).
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                    },
                    // refers to x.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                    })),
                // refers to .p2(y).
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[15], kTestCase.code),
                    },
                    // refers to y.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[17], kTestCase.code),
                    })),
                // refers to b1.
                T(
                    D{
                        IndexingFactType::kModuleInstance,
                        Anchor(kTestCase.expected_tokens[19], kTestCase.code),
                    },
                    // refers to z.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[21], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, InterfaceParameterExtraction) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"interface ",
       {kTag, "m"},
       "#(parameter ",
       {kTag, "x"},
       " = 1, parameter ",
       {kTag, "y"},
       " = 2) (input ",
       {kTag, "z"},
       ");\n ",
       {kTag, "bar"},
       " #(.",
       {kTag, "p1"},
       "(",
       {kTag, "x"},
       "), .",
       {kTag, "p2"},
       "(",
       {kTag, "y"},
       ")) ",
       {kTag, "b1"},
       "(",
       {kTag, "z"},
       ");\nendinterface"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to interface m.
        T(
            D{
                IndexingFactType::kInterface,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to module parameter x.
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to class parameter y.
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to class input x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                },
                // refers to .p1(x).
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                    },
                    // refers to x.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                    })),
                // refers to .p2(y).
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[15], kTestCase.code),
                    },
                    // refers to y.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[17], kTestCase.code),
                    })),
                // refers to b1.
                T(
                    D{
                        IndexingFactType::kModuleInstance,
                        Anchor(kTestCase.expected_tokens[19], kTestCase.code),
                    },
                    // refers to z.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[21], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ClassAsPort) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",
       {kTag, "m"},
       "(",
       {kTag, "class_type"},
       " ",
       {kTag, "x"},
       ");\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module m.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to class_type.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ProgramParameterExtraction) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"program ",
       {kTag, "m"},
       "#(parameter ",
       {kTag, "x"},
       " = 1, parameter ",
       {kTag, "y"},
       " = 2) (input ",
       {kTag, "z"},
       ");\n ",
       {kTag, "bar"},
       " #(.",
       {kTag, "p1"},
       "(",
       {kTag, "x"},
       "), .",
       {kTag, "p2"},
       "(",
       {kTag, "y"},
       ")) ",
       {kTag, "b1"},
       "(",
       {kTag, "z"},
       ");\nendprogram"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to program m.
        T(
            D{
                IndexingFactType::kProgram,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to module parameter x.
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to class parameter y.
            T(D{
                IndexingFactType::kParamDeclaration,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to class input x.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            }),
            // refers to bar.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                },
                // refers to .p1(x).
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                    },
                    // refers to x.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[13], kTestCase.code),
                    })),
                // refers to .p2(y).
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[15], kTestCase.code),
                    },
                    // refers to y.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[17], kTestCase.code),
                    })),
                // refers to b1.
                T(
                    D{
                        IndexingFactType::kModuleInstance,
                        Anchor(kTestCase.expected_tokens[19], kTestCase.code),
                    },
                    // refers to z.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[21], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, PackedAndUnpackedDimension) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg"},
       ";\nint [",
       {kTag, "k"},
       ":",
       {kTag, "y"},
       "] ",
       {kTag, "x"},
       " [",
       {kTag, "l"},
       ":",
       {kTag, "r"},
       "];\nendpackage\nmodule ",
       {kTag, "m"},
       ";\nint [",
       {kTag, "j"},
       ":",
       {kTag, "o"},
       "] ",
       {kTag, "v"},
       " [",
       {kTag, "e"},
       ":",
       {kTag, "t"},
       "];\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to k.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to y.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to x.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                },
                // refers to l.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                }),
                // refers to r.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                }))),
        // refers to module m.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            },
            // refers to j.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[15], kTestCase.code),
            }),
            // refers to o.
            T(D{
                IndexingFactType::kVariableReference,
                Anchor(kTestCase.expected_tokens[17], kTestCase.code),
            }),
            // refers to v.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[19], kTestCase.code),
                },
                // refers to e.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[21], kTestCase.code),
                }),
                // refers to t.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[23], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, FileIncludes) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase0 = {
      {"class ",
       {kTag, "my_class"},
       ";\n static int ",
       {kTag, "var5"},
       ";\n endclass"},
  };

  const std::string included_file_basename(
      verible::file::testing::RandomFileBasename("include-this"));
  const std::string quoted_included_file =
      absl::StrCat("\"", included_file_basename, "\"");

  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"`include ",
       {kTag, quoted_included_file},
       "\n module ",
       {kTag, "my_module"},
       "();\n initial begin\n$display(",
       {kTag, "my_class"},
       "::",
       {kTag, "var5"},
       ");\n "
       "end\nendmodule"},
  };

  IncludeTestProject project(kTestCase.code, included_file_basename,
                             kTestCase0.code, {::testing::TempDir()});

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.IncludeFile().ExpectedFileData(),
        // refers to class my_class.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase0.expected_tokens[1], kTestCase0.code),
            },
            // refers to int var5.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase0.expected_tokens[3], kTestCase0.code),
            }))),
      T(project.XUnit().ExpectedFileData(),
        // refers to include.
        T(D{
            IndexingFactType::kInclude,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(project.IncludeFile().source_file->ResolvedPath()),
        }),
        // refers to module my_module.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            },
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                // refers to $display(my_class::var5).
                T(D{
                    IndexingFactType::kMemberReference,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  // Print showing raw addresses instead of in-file byte-offsets.
  EXPECT_EQ(result_pair.left, nullptr) << *result_pair.left;
  EXPECT_EQ(result_pair.right, nullptr) << *result_pair.right;
}

TEST(FactsTreeExtractor, IncludedFileContainsParseError) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase0{
      {"parameter 7;"},  // parse error
  };

  const std::string included_file_basename(
      verible::file::testing::RandomFileBasename("include-this"));
  const std::string quoted_included_file =
      absl::StrCat("\"", included_file_basename, "\"");

  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"`include ",
       {kTag, quoted_included_file},
       "\n module ",
       {kTag, "my_module"},
       ";\nendmodule"},
  };

  IncludeTestProject project(kTestCase.code, included_file_basename,
                             kTestCase0.code, {::testing::TempDir()});

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      // No subtree from project.IncludeFile(), due to parse error.
      T(project.XUnit().ExpectedFileData(),
        // refers to include.
        T(D{
            IndexingFactType::kInclude,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(project.IncludeFile().source_file->ResolvedPath()),
        }),
        // refers to module my_module.
        T(D{
            IndexingFactType::kModule,
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        })));

  const auto facts_tree = project.Extract();

  // Check that a parse error was recorded.
  const auto statuses(project.Errors());
  ASSERT_EQ(statuses.size(), 1);
  EXPECT_EQ(statuses.front().code(), absl::StatusCode::kInvalidArgument);

  const auto result_pair = DeepEqual(facts_tree, expected);
  // Print showing raw addresses instead of in-file byte-offsets.
  EXPECT_EQ(result_pair.left, nullptr) << *result_pair.left;
  EXPECT_EQ(result_pair.right, nullptr) << *result_pair.right;
}

TEST(FactsTreeExtractor, IncludedFileNotFound) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase0{
      {"parameter int foo = 7;"},  // parse error
  };

  const std::string included_file_basename(
      verible::file::testing::RandomFileBasename("include-this"));

  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"`include ",
       {kTag, "\"wrong-wrong.wrong-file\""},
       "\n module ",
       {kTag, "my_module"},
       ";\nendmodule"},
  };

  IncludeTestProject project(kTestCase.code, included_file_basename,
                             kTestCase0.code, {::testing::TempDir()});

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      // included file was not found, and ignored
      T(project.XUnit().ExpectedFileData(),
        // No include node created.
        T(
            // refers to module my_module.
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            })));

  const auto facts_tree = project.Extract();

  // Confirm that we have an error status.
  const auto statuses(project.Errors());
  ASSERT_EQ(statuses.size(), 1);
  EXPECT_EQ(statuses.front().code(), absl::StatusCode::kNotFound);

  const auto result_pair = DeepEqual(facts_tree, expected);
  // Print showing raw addresses instead of in-file byte-offsets.
  EXPECT_EQ(result_pair.left, nullptr) << *result_pair.left;
  EXPECT_EQ(result_pair.right, nullptr) << *result_pair.right;
}

TEST(FactsTreeExtractor, FileIncludeSameFileTwice) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase0 = {
      {"class ",
       {kTag, "my_class"},
       ";\n static int ",
       {kTag, "var5"},
       ";\n endclass"},
  };

  const std::string included_file_basename(
      verible::file::testing::RandomFileBasename("include-this"));
  const std::string quoted_included_file =
      absl::StrCat("\"", included_file_basename, "\"");

  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"`include ",
       {kTag, quoted_included_file},
       "\n `include ",
       {kTag, quoted_included_file},  // repeated
       "\n module ",
       {kTag, "my_module"},
       "();\n initial begin\n$display(",
       {kTag, "my_class"},
       "::",
       {kTag, "var5"},
       ");\n "
       "end\nendmodule"},
  };

  IncludeTestProject project(kTestCase.code, included_file_basename,
                             kTestCase0.code, {::testing::TempDir()});

  const IndexingFactNode expected(                 //
      project.ExpectedFileListData(),              //
      T(project.IncludeFile().ExpectedFileData(),  //
                                                   // refers to class my_class.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase0.expected_tokens[1], kTestCase0.code),
            },
            // refers to int var5.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase0.expected_tokens[3], kTestCase0.code),
            }))),
      T(project.XUnit().ExpectedFileData(),  //
                                             // refers to include.
        T(D{
            IndexingFactType::kInclude,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(project.IncludeFile().source_file->ResolvedPath()),
        }),
        // refers to include (duplicate).
        T(D{
            IndexingFactType::kInclude,
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            Anchor(project.IncludeFile().source_file->ResolvedPath()),
        }),
        // refers to module my_module.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            },
            // anonymous scope for initial.
            T(
                D{
                    IndexingFactType::kAnonymousScope,
                    Anchor("anonymous-scope-0"),
                },
                // refers to $display(my_class::var5).
                T(D{
                    IndexingFactType::kMemberReference,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  // Print showing raw addresses instead of in-file byte-offsets.
  EXPECT_EQ(result_pair.left, nullptr) << *result_pair.left;
  EXPECT_EQ(result_pair.right, nullptr) << *result_pair.right;
}

TEST(FactsTreeExtractor, EnumTest) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg"},
       ";\nenum {",
       {kTag, "AA"},
       "} ",
       {kTag, "m_var"},
       ";\ntypedef enum {",
       {kTag, "BB"},
       "} ",
       {kTag, "var2"},
       ";\nendpackage\nmodule ",
       {kTag, "m"},
       "();\nenum {",
       {kTag, "CC"},
       "} ",
       {kTag, "m_var"},
       ";\ntypedef enum {",
       {kTag, "DD"},
       "} ",
       {kTag, "var3"},
       ";\nenum {",
       {kTag, "GG"},
       "=",
       {kTag, "y"},
       "[",
       {kTag, "idx"},
       "]} ",
       {kTag, "var6"},
       ";\ntypedef enum {",
       {kTag, "HH"},
       "=",
       {kTag, "yh"},
       "[",
       {kTag, "idx2"},
       "]} ",
       {kTag, "var5"},
       ";\n"
       "endmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to AA.
            T(D{
                IndexingFactType::kConstant,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to enum m_var.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            }),
            // refers to enum var.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[9], kTestCase.code),
            }),
            // refers to BB.
            T(D{
                IndexingFactType::kConstant,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            })),
        // refers to module m.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[11], kTestCase.code),
            },
            // refers to CC.
            T(D{
                IndexingFactType::kConstant,
                Anchor(kTestCase.expected_tokens[13], kTestCase.code),
            }),
            // refers to enum m_var.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[15], kTestCase.code),
            }),
            // refers to enum var2.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[19], kTestCase.code),
            }),
            // refers to DD.
            T(D{
                IndexingFactType::kConstant,
                Anchor(kTestCase.expected_tokens[17], kTestCase.code),
            }),
            // refers to GG.
            T(
                D{
                    IndexingFactType::kConstant,
                    Anchor(kTestCase.expected_tokens[21], kTestCase.code),
                },
                // refers to y.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[23], kTestCase.code),
                }),
                // refers to idx.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[25], kTestCase.code),
                })),
            // refers to enum var3.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[27], kTestCase.code),
            }),
            // refers to enum var5.
            T(D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[35], kTestCase.code),
            }),
            // refers to HH.
            T(
                D{
                    IndexingFactType::kConstant,
                    Anchor(kTestCase.expected_tokens[29], kTestCase.code),
                },
                // refers to yh.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[31], kTestCase.code),
                }),
                // refers to idx2.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[33], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, NonLiteralIncludeSafeFail) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      // no facts should be extracted for this include.
      {"`include `c\nmodule ",
       {kTag, "pkg"},
       ";\n struct {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module pkg.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, StructInModule) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",
       {kTag, "pkg"},
       ";\n struct {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module pkg.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ClassConstructor) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "my_class"},
       ";\nfunction ",
       {kTag, "new"},
       "(int ",
       {kTag, "x"},
       ");\n",
       {kTag, "x"},
       " = 1;\nendfunction\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class my_class.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to new.
            T(
                D{
                    IndexingFactType::kConstructor,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                }),
                // refers to x.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, StructInPackage) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg"},
       ";\n struct {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendpackage"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, UnionInModule) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",
       {kTag, "pkg"},
       ";\n union {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module pkg.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, UnionInPackage) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg"},
       ";\n union {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendpackage"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, UnionTypeInPackage) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg"},
       ";\n typedef union {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendpackage"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kStructOrUnion,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, UnionTypenModule) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",
       {kTag, "pkg"},
       ";\n typedef union {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module pkg.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kStructOrUnion,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, StructTypeInPackage) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"package ",
       {kTag, "pkg"},
       ";\n typedef struct {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendpackage"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package pkg.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kStructOrUnion,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, StructTypedefModule) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"module ",
       {kTag, "pkg"},
       ";\n typedef struct {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";\nendmodule"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to module pkg.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to var1.
            T(
                D{
                    IndexingFactType::kStructOrUnion,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, DataTypeReferenceInUnionType) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"typedef struct {\n ",
       {kTag, "some_type"},
       " ",
       {kTag, "var1"},
       ";}",
       {kTag, "var2"},
       ";"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to struct var1.
        T(
            D{
                IndexingFactType::kStructOrUnion,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            },
            // refers to some_type var1.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                },
                // refers to var1.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, StructInUnionType) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"typedef union {\n struct {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";}",
       {kTag, "var2"},
       ";"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to union var1.
        T(
            D{
                IndexingFactType::kStructOrUnion,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            },
            // refers to struct var2.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, StructInUnion) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"union {\n struct {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";}",
       {kTag, "var2"},
       ";"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to union var1.
        T(
            D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            },
            // refers to struct var2.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, UnionInStructType) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"typedef struct {\n union {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";}",
       {kTag, "var2"},
       ";"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to union var1.
        T(
            D{
                IndexingFactType::kStructOrUnion,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            },
            // refers to struct var2.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, UnionInStruct) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"struct {\n union {int ",
       {kTag, "xx"},
       ";} ",
       {kTag, "var1"},
       ";}",
       {kTag, "var2"},
       ";"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to union var1.
        T(
            D{
                IndexingFactType::kVariableDefinition,
                Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            },
            // refers to struct var2.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[1], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, TypedVariable) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "package ",
          {kTag, "m"},
          ";\n",
          {kTag, "some_type"},
          " ",
          {kTag, "var1"},
          ";\nendpackage\nmodule ",
          {kTag, "m"},
          "();\n",
          {kTag, "some_type1"},
          " ",
          {kTag, "var2"},
          ";\nendmodule",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to package m.
        T(
            D{
                IndexingFactType::kPackage,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to some_type.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to var1.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                }))),
        // refers to module m.
        T(
            D{
                IndexingFactType::kModule,
                Anchor(kTestCase.expected_tokens[7], kTestCase.code),
            },
            // refers to some_type1.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                },
                // refers to var2.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, FunctionNameAsQualifiedId) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "function ",
          {kTag, "pkg"},
          "::",
          {kTag, "f"},
          ";\nendfunction\ntask ",
          {kTag, "pkg"},
          "::",
          {kTag, "t"},
          ";\nendtask",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function pkg::f.
        T(D{
            IndexingFactType::kFunctionOrTask,
            Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            Anchor(kTestCase.expected_tokens[3], kTestCase.code),
        }),
        // refers to task pkg::t.
        T(D{
            IndexingFactType::kFunctionOrTask,
            Anchor(kTestCase.expected_tokens[5], kTestCase.code),
            Anchor(kTestCase.expected_tokens[7], kTestCase.code),
        })));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, PureVirtualFunction) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "env"},
       ";\npure virtual function int ",
       {kTag, "mod_if"},
       "( ",
       {kTag, "x"},
       ");\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class env.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to function mod_if.
            T(
                D{
                    IndexingFactType::kFunctionOrTaskForwardDeclaration,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ExternFunction) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "env"},
       ";\nextern function int ",
       {kTag, "mod_if"},
       "( ",
       {kTag, "x"},
       ");\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class env.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to function mod_if.
            T(
                D{
                    IndexingFactType::kFunctionOrTaskForwardDeclaration,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, ExternTask) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "env"},
       ";\nextern task ",
       {kTag, "mod_if"},
       "( ",
       {kTag, "x"},
       ");\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class env.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to function mod_if.
            T(
                D{
                    IndexingFactType::kFunctionOrTaskForwardDeclaration,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, PureVirtualTask) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "env"},
       ";\npure virtual task ",
       {kTag, "mod_if"},
       "( ",
       {kTag, "x"},
       ");\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class env.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to task mod_if.
            T(
                D{
                    IndexingFactType::kFunctionOrTaskForwardDeclaration,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, VirtualDataDeclaration) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {"class ",
       {kTag, "env"},
       " extends ",
       {kTag, "uvm_env"},
       ";\nvirtual ",
       {kTag, "mod_if"},
       " ",
       {kTag, "m_if"},
       ";\nendclass"},
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to class env.
        T(
            D{
                IndexingFactType::kClass,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to extends uvm_env.
            T(D{
                IndexingFactType::kExtends,
                Anchor(kTestCase.expected_tokens[3], kTestCase.code),
            }),
            // refers to mod_if.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to m_if.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, FunctionNamedArgument) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "function void ",
          {kTag, "f1"},
          "();\n",
          {kTag, "f2"},
          "(.",
          {kTag, "a"},
          "(",
          {kTag, "x"},
          "), .",
          {kTag, "b"},
          "(",
          {kTag, "y"},
          "));\nendfunction",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function f1.
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to f2.
            T(
                D{
                    IndexingFactType::kFunctionCall,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to a.
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                    },
                    // refers to x.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                    })),
                // refers to b.
                T(
                    D{
                        IndexingFactType::kNamedParam,
                        Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                    },
                    // refers to y.
                    T(D{
                        IndexingFactType::kVariableReference,
                        Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, FunctionPortPackedAndUnpackedDimsensions) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "function void ",
          {kTag, "f1"},
          "(int [",
          {kTag, "x"},
          ":",
          {kTag, "y"},
          "] ",
          {kTag, "t"},
          " [",
          {kTag, "l"},
          ":",
          {kTag, "r"},
          "]);\nendfunction",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function f1.
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to t.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                },
                // refers to x.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                }),
                // refers to y.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                }),
                // refers to l.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[9], kTestCase.code),
                }),
                // refers to r.
                T(D{
                    IndexingFactType::kVariableReference,
                    Anchor(kTestCase.expected_tokens[11], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, UserDefinedTypeFunctionPort) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "function void ",
          {kTag, "f1"},
          "(",
          {kTag, "foo"},
          " ",
          {kTag, "bar"},
          ");\nendfunction",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function f1.
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to foo.
            T(
                D{
                    IndexingFactType::kDataTypeReference,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                },
                // refers to bar.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, StructFunctionPort) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "function void ",
          {kTag, "f1"},
          "(struct {int ",
          {kTag, "foo"},
          ";} ",
          {kTag, "bar"},
          ");\nendfunction",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function f1.
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to bar.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                },
                // refers to foo.
                T(D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                })))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

TEST(FactsTreeExtractor, NestedStructFunctionPort) {
  constexpr int kTag = 1;  // value doesn't matter
  const verible::SyntaxTreeSearchTestCase kTestCase = {
      {
          "function void ",
          {kTag, "f1"},
          "(struct {struct {int ",
          {kTag, "y"},
          ";} ",
          {kTag, "foo"},
          ";} ",
          {kTag, "bar"},
          ");\nendfunction",
      },
  };

  SimpleTestProject project(kTestCase.code);

  const IndexingFactNode expected(  //
      project.ExpectedFileListData(),
      T(project.XUnit().ExpectedFileData(),
        // refers to function f1.
        T(
            D{
                IndexingFactType::kFunctionOrTask,
                Anchor(kTestCase.expected_tokens[1], kTestCase.code),
            },
            // refers to bar.
            T(
                D{
                    IndexingFactType::kVariableDefinition,
                    Anchor(kTestCase.expected_tokens[7], kTestCase.code),
                },
                // refers to foo.
                T(
                    D{
                        IndexingFactType::kVariableDefinition,
                        Anchor(kTestCase.expected_tokens[5], kTestCase.code),
                    },
                    // refers to y.
                    T(D{
                        IndexingFactType::kVariableDefinition,
                        Anchor(kTestCase.expected_tokens[3], kTestCase.code),
                    }))))));

  const auto facts_tree = project.Extract();

  const auto result_pair = DeepEqual(facts_tree, expected);
  const auto P = [&project](const T &t) { return project.TreePrinter(t); };
  EXPECT_EQ(result_pair.left, nullptr) << P(*result_pair.left);
  EXPECT_EQ(result_pair.right, nullptr) << P(*result_pair.right);
}

}  // namespace
}  // namespace kythe
}  // namespace verilog
